//=============================================================================
// SentinelMinigun
//=============================================================================
class SentinelMinigun extends SentinelWeapon;

var() float TraceRange;
var() int Damage;
var() float Momentum;
var() class<DamageType> DamageType;

var() float MaxAnglePerSecond;

var() sound WindUpSound, RunningSound, WindDownSound;

var SMinigunAttachment Gun;
var float BarrelRotationRate;
var rotator BarrelRotationL, BarrelRotationR;
var vector HitLoc, HitNorm;

replication
{
	unreliable if(Role == ROLE_Authority)
		BarrelRotationRate, HitLoc, HitNorm;
}

function InitFor(Sentinel S)
{
	Super.InitFor(S);

	if(WeaponAttachment != None)
		Gun = SMinigunAttachment(WeaponAttachment);
}

function float MaxRange()
{
	return TraceRange;
}

function Projectile FireAt(vector Start, vector BarrelDir, vector Aim)
{
	local Vector HitLocation, HitNormal;
	local Actor A;

	if(AmmoAmount < 1)
		return None;

	AmmoAmount -= AmmoPerFire;

	PlaySound(FireSound,, TransientSoundVolume * 2.0);
	Sentinel.MakeNoise(Loudness);
	FlashCount++;

	if(Level.NetMode != NM_DedicatedServer)
		FlashMuzzleFlash();

	Aim += VRand() * FRand() * Spread;
	Aim = normal(Aim);

	foreach TraceActors(class'Actor', A, HitLocation, HitNormal, Start + Aim * TraceRange, Start)
	{
		if(A == None)
			continue;

		//Spawn hit effects on world geometry.
		if(A.bWorldGeometry)
		{
			HitLoc = HitLocation;
			HitNorm = HitNormal;
			break;
		}

		//Don't hit own shield or instigator or self.
		if(A.IsA('ShieldSphere') && ShieldSphere(A).Shield.Sentinel == sentinel(owner))
		  continue;
		if(A.IsA('BigShieldSphere') && BigShieldSphere(A).Shield.myteam == sentinel.myteam)
		  continue;

		if(!A.bProjTarget)
			continue;

		A.TakeDamage(Damage, Sentinel.Instigator, HitLocation, Momentum * Aim, DamageType);

		if(A.bProjTarget)
			break;
	}

	return None;
}

simulated function FlashMuzzleFlash()
{
	if(FlashCount != LastFlashCount)
		Spawn(class'xHeavyWallHitEffect',,, HitLoc, Rotator(HitNorm));

	Super.FlashMuzzleFlash();
}

simulated function Tick(float DeltaTime)
{
	if(Gun == None && WeaponAttachment != None)
		Gun = SMinigunAttachment(WeaponAttachment);

	if(Gun == None)
		return;

	BarrelRotationL.Roll += BarrelRotationRate * DeltaTime;
	BarrelRotationL.Roll = BarrelRotationL.Roll % 65536;
	BarrelRotationR.Roll = -BarrelRotationL.Roll;
	Gun.SetBoneRotation('Bone_BarrelL', BarrelRotationL, 0, 1);
	Gun.SetBoneRotation('Bone_BarrelR', BarrelRotationR, 0, 1);
}

auto state Idle
{
	function BeginState()
	{
		BarrelRotationRate = 0;
	}

	function NewTarget(Pawn NewEnemy)
	{
		Super.NewTarget(NewEnemy);
		GotoState('WindingUp');
	}

	function Projectile FireAt(vector Start, vector BarrelDir, vector Aim)
	{
		return None;
	}
}

state WindingUp
{
	function BeginState()
	{
		SetTimer(2.0, false);
		PlaySound(WindUpSound, SLOT_Interact);
	}

	function Timer()
	{
		GotoState('Running');
	}

	function Tick(float DeltaTime)
	{
		BarrelRotationRate = TimerCounter * MaxAnglePerSecond * 0.5;
		Global.Tick(DeltaTime);
	}

	function Projectile FireAt(vector Start, vector BarrelDir, vector Aim)
	{
		return None;
	}

BEGIN:
	Sleep(GetSoundDuration(WindUpSound));
	AmbientSound = RunningSound;
}

state Running
{
	function BeginState()
	{
		AmbientSound = RunningSound;
		BarrelRotationRate = MaxAnglePerSecond;

		//In case target was eliminated before gun had a chance to spin up.
		if(Sentinel.Target == None)
			SetTimer(1.0, false);
	}

	function EndState()
	{
		AmbientSound = None;
	}

	//Wait a second before spinning down, in case a new targe presents itself.
	function SetWaiting()
	{
		Super.SetWaiting();
		SetTimer(1.0, false);
	}

	function Timer()
	{
		if(Sentinel.Target == None)
			GotoState('WindingDown');
		else
			SetTimer(1.0, false);
	}
}

state WindingDown
{
	function BeginState()
	{
		SetTimer(2.0, false);
		PlaySound(WindDownSound, SLOT_Interact);
	}

	function Timer()
	{
		GotoState('Idle');
	}

	function Tick(float DeltaTime)
	{
		BarrelRotationRate = (1 - TimerCounter * 0.5) * MaxAnglePerSecond;
		Global.Tick(DeltaTime);
	}

	function Projectile FireAt(vector Start, vector BarrelDir, vector Aim)
	{
		return None;
	}
}

defaultproperties
{
     TraceRange=10000.000000
     Damage=5
     Momentum=5000.000000
     DamageType=Class'taskforces.DamTypeSentinelMinigun'
     MaxAnglePerSecond=109226.664063
     WindUpSound=Sound'TFMedia.SMinigunWindUp'
     RunningSound=Sound'TFMedia.SMinigunLoop'
     WindDownSound=Sound'TFMedia.SMinigunWindDown'
     FireRate=0.100000
     FireSound=Sound'WeaponSounds.BaseFiringSounds.BMiniGunAltFire'
     ProjSpawnOffset=56.000000
     Spread=0.050000
     WeaponAttachmentClass=Class'taskforces.SMinigunAttachment'
     WeaponOffset=(X=450.000000,Y=70.000000)
     MuzzleFlashClass=Class'taskforces.SMinigunMuzFlash'
     FlashScale=3.000000
     Loudness=0.800000
     MaxAmmo=1
     AmmoAmount=1
     AmmoCost=1
     UpgradeName="Minigun"
     Description="A conventional kinetic weapon, the minigun attachment is most effective against the more agile foe who may dodge more powerful but slower projectiles from other weapons. One of the most common uses of the minigun attachment is as an anti-aircraft weapon.||The Sentinel minigun incorporates an unusual twin-triplet barrel design, resulting in faster spin-up time compared to other heavy minigun designs, but it is still wise to place a minigun equipped Sentinel such that it has advance warning of an approaching enemy so it can spin-up ready to fire immediately."
     ActivateSound=Sound'WeaponSounds.Minigun.SwitchToMiniGun'
}
